﻿using System.Security.Cryptography;
using System.Text;

/// <summary>
/// 实用辅助工具
/// </summary>
public static class UtilityHelper
{
    private static readonly UTF8Encoding uTF8Encoding = new UTF8Encoding(false);
    /// <summary>
    /// Base16基础字符
    /// </summary>
    private static readonly char[] _CharCodesBase16 = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];

    /// <summary>
    /// Base32基础字符
    /// </summary>
    private static readonly char[] _CharCodesBase32 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '='];

    /// <summary>
    /// Base64基础字符
    /// </summary>
    private static readonly char[] _CharCodesBase64 = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='];

    /// <summary>
    /// Base64基础字符
    /// </summary>
    private static readonly char[] _CharCodesBase64Url = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', '='];

    /// <summary>
    /// 默认密文模式
    /// </summary>
    private const CipherMode _CipherMode = CipherMode.CBC;

    /// <summary>
    /// 默认填充方式
    /// </summary>
    private const PaddingMode _PaddingMode = PaddingMode.PKCS7;

    /// <summary>
    /// AES密钥
    /// </summary>
    private static byte[] _AesKey = uTF8Encoding.GetBytes("209146834CE092F4B5399D0F1C7FD13D"); // Blizzard MD5

    /// <summary>
    /// HMAC密钥
    /// </summary>
    private static byte[] _HmacKey = uTF8Encoding.GetBytes("209146834CE092F4B5399D0F1C7FD13D"); // Blizzard MD5

    /// <summary>
    /// 针对bool类型的扩展方法。
    /// 如果 true。则返回当前时间
    /// 如果是 false。则返回系统预设的最小时间
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static DateTime GetTime(this bool o)
    {
        if (o) return DateTime.Now;
        return new DateTime(1970, 1, 1, 0, 0, 0);
    }

    /// <summary>
    /// 转bool(默认开启数字类型不等于0时就算作true)
    /// </summary>
    /// <param name="o"></param>
    /// <param name="nonZeroNumberIsTrue"></param>
    /// <returns></returns>
    public static bool ObjToBoolean(this object o, bool nonZeroNumberIsTrue = true)
    {
        string t = string.Empty;
        if (nonZeroNumberIsTrue && o.ObjToDouble(out t) != 0.0) return true;
        bool.TryParse(t, out var re);
        return re;
    }

    /// <summary>
    /// 转byte
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static byte ObjToByte(this object o)
    {
        byte.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转char
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static char ObjToChar(this object o)
    {
        char.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转DateTime
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static DateTime ObjToDateTime(this object o)
    {
        DateTime.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转decimal
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static decimal ObjToDecimal(this object o)
    {
        decimal.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转double
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static double ObjToDouble(this object o)
    {
        double.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转double
    /// </summary>
    /// <param name="o"></param>
    /// <param name="s"></param>
    /// <returns></returns>
    private static double ObjToDouble(this object o, out string s)
    {
        s = o.ObjToString();
        double.TryParse(s, out var re);
        return re;
    }

    /// <summary>
    /// 转short
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static short ObjToInt16(this object o)
    {
        short.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转int
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static int ObjToInt32(this object o)
    {
        int.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转long
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static long ObjToInt64(this object o)
    {
        long.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转sbyte
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static sbyte ObjToSByte(this object o)
    {
        sbyte.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转float
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static float ObjToSingle(this object o)
    {
        float.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转string
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static string ObjToString(this object o)
    {
        if (o != null)
        {
            string tmp = o.ToString();
            if (string.IsNullOrWhiteSpace(tmp))
            {
                return "";
            }
            return tmp;
        }
        return "";
    }

    /// <summary>
    /// 转ushort
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static ushort ObjToUInt16(this object o)
    {
        ushort.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转uint
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static uint ObjToUInt32(this object o)
    {
        uint.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 转ulong
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public static ulong ObjToUInt64(this object o)
    {
        ulong.TryParse(o.ObjToString(), out var re);
        return re;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="timeSpan"></param>
    /// <returns></returns>
    public static string ToStringFormat(this TimeSpan timeSpan)
    {
        var days = timeSpan.Days;
        var hours = timeSpan.Hours;
        var minutes = timeSpan.Minutes;
        var seconds = timeSpan.Seconds;
        var millseconds = timeSpan.Milliseconds;
        if (days > 0)
        {
            return $"{days}天{hours}时{minutes}分{seconds}秒{millseconds}";
        }
        else if (hours > 0)
        {
            return $"{hours}时{minutes}分{seconds}秒{millseconds}";
        }
        else if (minutes > 0)
        {
            return $"{minutes}分{seconds}秒{millseconds}";
        }
        else if (seconds > 0)
        {
            return $"{seconds}秒{millseconds}";
        }
        else if (millseconds > 0)
        {
            return $"{millseconds}";
        }
        else
        {
            return "";
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="bytes"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    public static string BytesToString(this byte[] bytes, Encoding encoding = null)
    {
        return bytes == null ? "" : (encoding ?? uTF8Encoding).GetString(bytes);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="str"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    public static byte[] StringToBytes(this string str, Encoding encoding = null)
    {
        return string.IsNullOrWhiteSpace(str) ? null : (encoding ?? uTF8Encoding).GetBytes(str);
    }

    /// <summary>
    /// Base16编码
    /// </summary>
    /// <param name="input"></param>
    /// <param name="charCodes"></param>
    /// <returns></returns>
    public static string Base16Encode(this byte[] input, char[] charCodes = null)
    {
        if (input == null || input.Length == 0)
            return string.Empty;
        if (charCodes == null || charCodes.Length < 16) charCodes = _CharCodesBase16;
        int totalLength = input.Length * 2;
        Span<char> output = new char[totalLength];

        int buffer = 0;
        int bitsLeft = 0;
        int outputIndex = 0;

        foreach (byte b in input)
        {
            buffer = (buffer << 8) | b;
            bitsLeft += 8;

            while (bitsLeft >= 4)
            {
                output[outputIndex++] = charCodes[(buffer >> (bitsLeft - 4)) & 0x0F];
                bitsLeft -= 4;
            }
        }

        return new string(output);
    }

    /// <summary>
    /// Base16解码
    /// </summary>
    /// <param name="input"></param>
    /// <param name="charCodes"></param>
    /// <returns></returns>
    public static byte[] Base16Decode(this string input, char[] charCodes = null)
    {
        if (input == null || input.Length == 0) return default;
        input = input.TrimEnd('=');
        if (charCodes == null || charCodes.Length < 16) charCodes = _CharCodesBase16;
        int[] indexMap = new int[char.MaxValue + 1];
        Array.Fill(indexMap, -1);
        for (int i = 0; i < charCodes.Length; i++) indexMap[charCodes[i]] = i;
        byte[] result = new byte[input.Length / 2];

        int buffer = 0;
        int bitsLeft = 0;
        int index = 0;

        foreach (char c in input)
        {
            buffer = (buffer << 4) | indexMap[c];
            bitsLeft += 4;

            if (bitsLeft >= 8)
            {
                result[index++] = (byte)(buffer >> (bitsLeft - 8));
                bitsLeft -= 8;
            }
        }

        return result;
    }

    /// <summary>
    /// Base32编码
    /// </summary>
    /// <param name="input"></param>
    /// <param name="charCodes"></param>
    /// <param name="isPadding"></param>
    /// <returns></returns>
    public static string Base32Encode(this byte[] input, char[] charCodes = null, bool isPadding = true)
    {
        if (input == null || input.Length == 0)
            return string.Empty;
        if (charCodes == null || charCodes.Length <= 32) charCodes = _CharCodesBase32;
        int charCount = (input.Length * 8 + 4) / 5;
        int totalLength = (charCount + 7) / 8 * 8;
        Span<char> output = new char[isPadding ? totalLength : charCount];

        int buffer = 0;
        int bitsLeft = 0;
        int outputIndex = 0;

        foreach (byte b in input)
        {
            buffer = (buffer << 8) | b;
            bitsLeft += 8;

            while (bitsLeft >= 5)
            {
                output[outputIndex++] = charCodes[(buffer >> (bitsLeft - 5)) & 0x1F];
                bitsLeft -= 5;
            }
        }

        if (bitsLeft > 0)
        {
            output[outputIndex++] = charCodes[(buffer << (5 - bitsLeft)) & 0x1F];
        }

        if (isPadding == false) return new string(output);

        while (outputIndex < totalLength)
        {
            output[outputIndex++] = charCodes[32];
        }

        return new string(output);
    }

    /// <summary>
    /// Base32解码
    /// </summary>
    /// <param name="input"></param>
    /// <param name="charCodes"></param>
    /// <returns></returns>
    public static byte[] Base32Decode(this string input, char[] charCodes = null)
    {
        if (input == null || input.Length == 0) return default;
        input = input.TrimEnd('=');
        if (charCodes == null || charCodes.Length <= 32) charCodes = _CharCodesBase32;
        int[] indexMap = new int[char.MaxValue + 1];
        Array.Fill(indexMap, -1);
        for (int i = 0; i < charCodes.Length; i++) indexMap[charCodes[i]] = i;
        byte[] result = new byte[input.Length * 5 / 8];

        int buffer = 0;
        int bitsLeft = 0;
        int index = 0;

        foreach (char c in input)
        {
            buffer = (buffer << 5) | indexMap[c];
            bitsLeft += 5;

            if (bitsLeft >= 8)
            {
                result[index++] = (byte)(buffer >> (bitsLeft - 8));
                bitsLeft -= 8;
            }
        }

        return result;
    }

    /// <summary>
    /// Base64编码
    /// </summary>
    /// <param name="input"></param>
    /// <param name="charCodes"></param>
    /// <param name="isPadding"></param>
    /// <returns></returns>
    public static string Base64Encode(this byte[] input, char[] charCodes = null, bool isPadding = true)
    {
        if (input == null || input.Length == 0)
            return string.Empty;
        if (charCodes == null || charCodes.Length <= 64) charCodes = _CharCodesBase64;
        int charCount = (input.Length * 4 + 2) / 3;
        int totalLength = (charCount + 3) / 4 * 4;
        Span<char> output = new char[isPadding ? totalLength : charCount];

        int buffer = 0;
        int bitsLeft = 0;
        int outputIndex = 0;

        foreach (byte b in input)
        {
            buffer = (buffer << 8) | b;
            bitsLeft += 8;

            while (bitsLeft >= 6)
            {
                output[outputIndex++] = charCodes[(buffer >> (bitsLeft - 6)) & 0x3F];
                bitsLeft -= 6;
                // buffer &= (1 << bitsLeft) - 1;
            }
        }

        if (bitsLeft > 0)
        {
            output[outputIndex++] = charCodes[(buffer << (6 - bitsLeft)) & 0x3F];
        }

        if (isPadding == false) return new string(output);

        while (outputIndex < totalLength)
        {
            output[outputIndex++] = charCodes[64];
        }

        return new string(output);
    }

    /// <summary>
    /// Base64解码
    /// </summary>
    /// <param name="input"></param>
    /// <param name="charCodes"></param>
    /// <returns></returns>
    public static byte[] Base64Decode(this string input, char[] charCodes = null)
    {
        if (input == null || input.Length == 0) return default;
        input = input.TrimEnd('=');
        if (charCodes == null || charCodes.Length <= 64) charCodes = _CharCodesBase64;
        int[] indexMap = new int[char.MaxValue + 1];
        Array.Fill(indexMap, -1);
        for (int i = 0; i < charCodes.Length; i++) indexMap[charCodes[i]] = i;
        byte[] result = new byte[input.Length * 3 / 4];

        int buffer = 0;
        int bitsLeft = 0;
        int index = 0;

        foreach (char c in input)
        {
            buffer = (buffer << 6) | indexMap[c];
            bitsLeft += 6;

            if (bitsLeft >= 8)
            {
                result[index++] = (byte)(buffer >> (bitsLeft - 8));
                bitsLeft -= 8;
                // buffer &= (1 << bitsLeft) - 1;
            }
        }

        return result;
    }

    /// <summary>
    /// Base64Url编码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static string Base64UrlEncode(this byte[] input)
    {
        if (input == null || input.Length == 0)
            return string.Empty;
        char[] charCodes = _CharCodesBase64Url;
        int charCount = (input.Length * 4 + 2) / 3;
        Span<char> output = new char[charCount];

        int buffer = 0;
        int bitsLeft = 0;
        int outputIndex = 0;

        foreach (byte b in input)
        {
            buffer = (buffer << 8) | b;
            bitsLeft += 8;

            while (bitsLeft >= 6)
            {
                output[outputIndex++] = charCodes[(buffer >> (bitsLeft - 6)) & 0x3F];
                bitsLeft -= 6;
                // buffer &= (1 << bitsLeft) - 1;
            }
        }

        if (bitsLeft > 0)
        {
            output[outputIndex++] = charCodes[(buffer << (6 - bitsLeft)) & 0x3F];
        }

        return new string(output);
    }

    /// <summary>
    /// Base64Url解码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static byte[] Base64UrlDecode(this string input)
    {
        if (input == null || input.Length == 0) return default;
        input = input.TrimEnd('=');
        char[] charCodes = _CharCodesBase64Url;
        int[] indexMap = new int[char.MaxValue + 1];
        Array.Fill(indexMap, -1);
        for (int i = 0; i < charCodes.Length; i++) indexMap[charCodes[i]] = i;
        byte[] result = new byte[input.Length * 3 / 4];

        int buffer = 0;
        int bitsLeft = 0;
        int index = 0;

        foreach (char c in input)
        {
            buffer = (buffer << 6) | indexMap[c];
            bitsLeft += 6;

            if (bitsLeft >= 8)
            {
                result[index++] = (byte)(buffer >> (bitsLeft - 8));
                bitsLeft -= 8;
                // buffer &= (1 << bitsLeft) - 1;
            }
        }

        return result;
    }

    /// <summary>
    /// MD5
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string MD5Encrypt(this string input)
    {
        return input.MD5Encrypt(uTF8Encoding);
    }

    /// <summary>
    /// MD5
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string MD5Encrypt(this string input, Encoding encoding)
    {
        using (MD5 md5 = MD5.Create())
        {
            return md5.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA1
    /// </summary>
    /// <param name="content"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string SHA1Encrypt(this string content)
    {
        return content.SHA1Encrypt(uTF8Encoding);
    }

    /// <summary>
    /// SHA1
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string SHA1Encrypt(this string input, Encoding encoding)
    {
        using (SHA1 sha1 = SHA1.Create())
        {
            return sha1.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA256
    /// </summary>
    /// <param name="content"></param>
    /// <returns></returns>
    public static string SHA256Encrypt(this string content)
    {
        return content.SHA256Encrypt(uTF8Encoding);
    }

    /// <summary>
    /// SHA256
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    public static string SHA256Encrypt(this string input, Encoding encoding)
    {
        using (SHA256 sha256 = SHA256.Create())
        {
            return sha256.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA384
    /// </summary>
    /// <param name="content"></param>
    /// <returns></returns>
    public static string SHA384Encrypt(this string content)
    {
        return content.SHA384Encrypt(uTF8Encoding);
    }

    /// <summary>
    /// SHA384
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    public static string SHA384Encrypt(this string input, Encoding encoding)
    {
        using (SHA384 sha384 = SHA384.Create())
        {
            return sha384.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA512
    /// </summary>
    /// <param name="content"></param>
    /// <returns></returns>
    public static string SHA512Encrypt(this string content)
    {
        return content.SHA512Encrypt(uTF8Encoding);
    }

    /// <summary>
    /// SHA512
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <returns></returns>
    public static string SHA512Encrypt(this string input, Encoding encoding)
    {
        using (SHA512 sha512 = SHA512.Create())
        {
            return sha512.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA3_256
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string SHA3_256Encrypt(this string content, string key = null)
    {
        return content.SHA3_256Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// SHA3_256
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string SHA3_256Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (SHA3_256 sha3_256 = SHA3_256.Create())
        {
            return sha3_256.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA3_384
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string SHA3_384Encrypt(this string content, string key = null)
    {
        return content.SHA3_384Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// SHA3_384
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string SHA3_384Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (SHA3_384 sha3_384 = SHA3_384.Create())
        {
            return sha3_384.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// SHA3_512
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string SHA3_512Encrypt(this string content, string key = null)
    {
        return content.SHA3_512Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// SHA3_512
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string SHA3_512Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (SHA3_512 sha3_512 = SHA3_512.Create())
        {
            return sha3_512.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACMD5
    /// </summary>
    /// <param name="input"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string HMACMD5Encrypt(this string input, string key = null)
    {
        return input.HMACMD5Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACMD5
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string HMACMD5Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (HMACMD5 hmacmd5 = new HMACMD5(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacmd5.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA1
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string HMACSHA1Encrypt(this string content, string key = null)
    {
        return content.HMACSHA1Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA1
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    [Obsolete("已经被证实是不再安全的散列方案，不建议继续使用。")]
    public static string HMACSHA1Encrypt(this string input, Encoding encoding, string key)
    {
        using (HMACSHA1 hmacsha1 = new HMACSHA1(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha1.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA256
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA256Encrypt(this string content, string key = null)
    {
        return content.HMACSHA256Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA256
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA256Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (HMACSHA256 hmacsha256 = new HMACSHA256(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha256.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA384
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA384Encrypt(this string content, string key = null)
    {
        return content.HMACSHA384Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA384
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA384Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (HMACSHA384 hmacsha384 = new HMACSHA384(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha384.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA512
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA512Encrypt(this string content, string key)
    {
        return content.HMACSHA512Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA512
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA512Encrypt(this string input, Encoding encoding, string key)
    {
        using (HMACSHA512 hmacsha512 = new HMACSHA512(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha512.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA3_256
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA3_256Encrypt(this string content, string key = null)
    {
        return content.HMACSHA3_256Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA3_256
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA3_256Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (HMACSHA3_256 hmacsha3_256 = new HMACSHA3_256(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha3_256.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA3_384
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA3_384Encrypt(this string content, string key = null)
    {
        return content.HMACSHA3_384Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA3_384
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA3_384Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (HMACSHA3_384 hmacsha3_384 = new HMACSHA3_384(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha3_384.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// HMACSHA3_512
    /// </summary>
    /// <param name="content"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA3_512Encrypt(this string content, string key = null)
    {
        return content.HMACSHA3_512Encrypt(uTF8Encoding, key);
    }

    /// <summary>
    /// HMACSHA3_512
    /// </summary>
    /// <param name="input"></param>
    /// <param name="encoding"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string HMACSHA3_512Encrypt(this string input, Encoding encoding, string key = null)
    {
        using (HMACSHA3_512 hmacsha3_512 = new HMACSHA3_512(
            string.IsNullOrWhiteSpace(key)
            ? _HmacKey
            : uTF8Encoding.GetBytes(key)))
        {
            return hmacsha3_512.ComputeHash(encoding.GetBytes(input)).Base16Encode();
        }
    }

    /// <summary>
    /// AES加密
    /// </summary>
    /// <param name="input"></param>
    /// <param name="cipherMode"></param>
    /// <param name="paddingMode"></param>
    /// <param name="key"></param>
    /// <param name="iv"></param>
    /// <returns></returns>
    public static byte[] AESEncrypt(this byte[] input, byte[] key = null, byte[] iv = null, CipherMode? cipherMode = null, PaddingMode? paddingMode = null)
    {
        if (input == null) return default;
        using (Aes aes = Aes.Create())
        {
            aes.Key = key ?? _AesKey;
            aes.IV = iv ?? _AesKey.AsMemory(8, 16).ToArray();//[.. _AesKey.Skip(8).Take(16)];
            aes.Mode = cipherMode ?? _CipherMode;
            aes.Padding = paddingMode ?? _PaddingMode;
            using (ICryptoTransform ct = aes.CreateEncryptor())
            {
                var res = ct.TransformFinalBlock(input, 0, input.Length);
                return res;
            }
        }
    }

    /// <summary>
    /// AES解密
    /// </summary>
    /// <param name="input"></param>
    /// <param name="cipherMode"></param>
    /// <param name="paddingMode"></param>
    /// <param name="key"></param>
    /// <param name="iv"></param>
    /// <returns></returns>
    public static byte[] AESDecrypt(this byte[] input, byte[] key = null, byte[] iv = null, CipherMode? cipherMode = null, PaddingMode? paddingMode = null)
    {
        if (input == null) return default;
        using (Aes aes = Aes.Create())
        {
            aes.Key = key ?? _AesKey;
            aes.IV = iv ?? _AesKey.AsMemory(8, 16).ToArray();//[.. _AesKey.Skip(8).Take(16)];
            aes.Mode = cipherMode ?? _CipherMode;
            aes.Padding = paddingMode ?? _PaddingMode;
            using (ICryptoTransform ct = aes.CreateDecryptor())
            {
                var res = ct.TransformFinalBlock(input, 0, input.Length);
                return res;
            }
        }
    }

    /// <summary>
    /// 微信小程序AES解密
    /// </summary>
    /// <param name="input"></param>
    /// <param name="key"></param>
    /// <param name="iv"></param>
    /// <returns></returns>
    public static string WxAppletsAESDecrypt(string input, string key, string iv)
    {
        byte[] inputBytes = input.Replace(" ", "+").Base64Decode();
        byte[] keyBytes = key.Replace(" ", "+").Base64Decode();
        byte[] ivBytes = iv.Replace(" ", "+").Base64Decode();
        byte[] resultBytes = inputBytes.AESDecrypt(keyBytes.ToArray(), ivBytes.ToArray(), CipherMode.CBC, PaddingMode.PKCS7);
        return resultBytes == null ? "" : uTF8Encoding.GetString(resultBytes);
    }

    /// <summary>
    /// 分割成'',''的形式
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="ts"></param>
    /// <returns></returns>
    public static string SplitByCommaWithSingleQuotes<T>(this IEnumerable<T> ts)
    {
        if (ts == null || ts.Count() == 0) return "";
        return string.Join(',', ts.Select(o => $"'{o}'"));
    }
}
